home *** CD-ROM | disk | FTP | other *** search
- /*
- * This file is part of ixemul.library for the Amiga.
- * Copyright (C) 1991, 1992 Markus M. Wild
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Library General Public
- * License as published by the Free Software Foundation; either
- * version 2 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with this library; if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * __load_seg.c,v 1.1.1.1 1994/04/04 04:30:54 amiga Exp
- *
- * __load_seg.c,v
- * Revision 1.1.1.1 1994/04/04 04:30:54 amiga
- * Initial CVS check in.
- *
- * Revision 1.5 1992/09/14 01:38:35 mwild
- * fix a bug with #! expansion (forgot separating /)
- *
- * Revision 1.4 1992/08/09 20:37:07 amiga
- * change to use 2.x header files by default
- *
- * Revision 1.3 1992/07/04 19:02:39 mwild
- * fix typo, the buffer for interpreter-expansion was ways too small...
- *
- * Revision 1.2 1992/05/22 01:45:00 mwild
- * rewrote interpreter expansion, `should' now work as expected
- *
- * Revision 1.1 1992/05/14 19:55:40 mwild
- * Initial revision
- *
- */
-
- #define _KERNEL
- #include "ixemul.h"
- #include "kprintf.h"
-
- #include <ctype.h>
- #include <string.h>
-
- /* 2.0 support */
- #include <utility/tagitem.h>
- #include <dos/dostags.h>
-
- extern void *kmalloc (size_t size);
-
- static struct my_seg *try_load_seg (BPTR lock, char *name, char **args, char *progname);
-
- char *basename(char *tmp)
- {
- char *cp = rindex (tmp, '/');
-
- if (cp)
- return cp + 1;
- if ((cp = index (tmp, ':')))
- return cp + 1;
- return tmp;
- }
-
- static struct my_seg *
- check_resident (char *tmp)
- {
- struct my_seg *res = 0;
- struct Segment *seg;
- char *base;
-
- /* big problem: Commo only stores the bare names in the resident
- list. So we have to truncate to the filename part, and so lose
- the ability to explicitly load the disk version even if a
- resident version is installed */
-
-
- base = basename(tmp);
- Forbid();
- seg = FindSegment(base, 0, 0);
- if (seg)
- {
- /* strange they didn't provide a function for this... */
- if (seg->seg_UC >= 0)
- seg->seg_UC++;
- }
- Permit();
-
- if (seg && (res = (struct my_seg *)kmalloc(sizeof(*res) + strlen(tmp) + 1)))
- {
- res->segment = seg->seg_Seg;
- res->type = RESSEG;
- res->priv = (u_int)seg;
- res->name = (char *)&res[1];
- strcpy(res->name, tmp);
- }
- else if (seg)
- {
- Forbid();
- if (seg->seg_UC > 0)
- seg->seg_UC--;
- Permit();
- }
-
- return res;
- }
-
-
- static struct my_seg *
- check_loadseg (char *tmp)
- {
- struct my_seg *res = 0;
- BPTR seg;
- int err;
-
- /* Note that LoadSeg returns NULL and sets IoErr() to 0 if the file it
- is trying to load is less than 4 bytes long. */
- seg = LoadSeg (tmp);
- if (seg && (res = kmalloc (sizeof (*res) + strlen(tmp) + 1)))
- {
- res->segment = seg;
- res->type = LOADSEG;
- res->priv = seg;
- res->name = (char *)&res[1];
- strcpy(res->name, tmp);
- }
- else if (seg)
- {
- UnLoadSeg (seg);
- errno = ENOMEM;
- return NULL;
- }
- err = IoErr();
- errno = (err ? __ioerr_to_errno(err) : ENOENT);
-
- if (errno == EINVAL)
- errno = ENOENT;
-
- return res;
- }
-
-
- void
- __free_seg (BPTR *seg)
- {
- struct my_seg *ms;
-
- ms = (struct my_seg *) seg;
-
- if (ms->type == RESSEG)
- {
- struct Segment *s = (struct Segment *) ms->priv;
-
- Forbid ();
- if (s->seg_UC > 0)
- s->seg_UC--;
- Permit ();
- }
- else
- UnLoadSeg (ms->priv);
-
- kfree (ms);
- }
-
-
- /*
- * This function does what LoadSeg() does, and a little bit more ;-)
- * Besides walking the PATH of the user, we try to do interpreter expansion as
- * well. But, well, we do it a little bit different then a usual Amiga-shell.
- * We check the magic cookies `#!' and `;!', and if found, run the interpreter
- * specified on this first line of text. This does *not* depend on any script
- * bit set!
- */
-
- /*
- * IMPORTANT: only call this function with all signals masked!!!
- */
-
- /*
- * name: the name of the command to load. Can be relative to installed PATH
- * args: if set, a string to the first part of an expanded command is stored
- */
-
- BPTR *
- __load_seg (char *name, char **args)
- {
- BPTR lock;
- struct my_seg *seg;
-
- /* perhaps the name is vanilla enough, so that even LoadSeg() groks it? */
- if (args) *args = 0;
-
- seg = check_resident (name);
-
- if (! seg)
- seg = check_loadseg (name);
-
- if (seg)
- return &seg->segment;
-
- if (errno != ENOENT)
- return 0;
-
- /* try to lock the file (using __lock() provides full path-parsing ;-)) */
- lock = __lock (name, ACCESS_READ);
- if (lock)
- {
- int err;
- BPTR parent = ParentDir(lock);
- struct FileInfoBlock *fib;
-
- fib = alloca (sizeof(*fib) + 2);
- fib = LONG_ALIGN (fib);
- if (Examine (lock, fib))
- seg = try_load_seg (parent, fib->fib_FileName, args, name);
- else
- seg = try_load_seg (parent, basename(name), args, name);
- err = errno;
-
- __unlock (parent);
- __unlock (lock);
-
- errno = err;
- if (!seg && errno != ENOENT)
- return 0;
- }
-
- /* now we may have a valid segment */
- if (seg)
- return &seg->segment;
-
- /* if the command was specified with some kind of path, for example with a
- * device or a directory in it, we don't run it thru the PATH expander
- */
- if (strpbrk (name, ":/"))
- {
- errno = ENOENT;
- return 0;
- }
-
- /* so the command is not directly addressable, but perhaps it's in our PATH? */
- {
- struct Process *me = (struct Process *)(SysBase->ThisTask);
- struct CommandLineInterface *cli;
-
- /* but we need a valid CLI then */
- if ((cli = BTOCPTR (me->pr_CLI)))
- {
- struct path_element {
- BPTR next;
- BPTR lock;
- } *lock_list;
- BPTR ocd;
-
- for (lock_list = BTOCPTR (cli->cli_CommandDir);
- lock_list;
- lock_list = BTOCPTR (lock_list->next))
- {
- ocd = CurrentDir (lock_list->lock);
- seg = try_load_seg (lock_list->lock, name, args, name);
- CurrentDir(ocd);
- if (seg)
- break;
- if (errno != ENOENT)
- return 0;
- }
- }
- }
-
- if (seg)
- return &seg->segment;
-
- errno = ENOENT;
- KPRINTF (("&errno = %lx, errno = %ld\n", &errno, errno));
- return 0;
- }
-
- static struct my_seg *
- try_load_seg (BPTR parent, char *name, char **args, char *progname)
- {
- BPTR ocd, tmpcd;
- short oroot;
- struct my_seg *seg;
-
- if (args) *args = 0;
- ocd = CurrentDir (parent);
- oroot = u.u_is_root;
- u.u_is_root = 0;
-
- seg = check_loadseg (name);
-
- if (!seg && errno != ENOENT)
- {
- CurrentDir(ocd);
- u.u_is_root = oroot;
- return 0;
- }
-
- /* try to do interpreter - expansion, but only if args is non-zero */
- if (!seg && args)
- {
- int fd, n;
- char magic[5];
- struct stat stb;
-
- if (syscall (SYS_stat, name, &stb) == 0 && S_ISREG (stb.st_mode))
- {
- if ((fd = syscall (SYS_open, name, 0)) >= 0)
- {
- /*
- * If the .key line of an AmigaDOS script isn't the first
- * line of the script, the AmigaDOS shell gets very confused.
- * Therefore, we skip the first line if it begins with .key,
- * and we test the second line for #! or ;!.
- */
- if ((n = syscall (SYS_read, fd, magic, 4)) == 4)
- {
- magic[4] = 0;
- if (!strcasecmp(magic, ".key"))
- {
- n = 0;
- /* skip this line */
- while (syscall (SYS_read, fd, magic, 1) == 1)
- if (magic[0] == '\n')
- {
- n = syscall (SYS_read, fd, magic, 4);
- break;
- }
- }
- }
- if (n >= 2 && (magic[0] == '#' || magic[0] == ';') && magic[1] == '!')
- {
- char interp[MAXPATHLEN + 1], *interp_start;
-
- interp[0] = magic[2];
- interp[1] = magic[3];
- n -= 2;
- n = n + syscall (SYS_read, fd, interp + n, MAXINTERP - n);
- if (n > 0)
- {
- char *cp, ch;
- char *interp_path;
-
- /* okay.. got one.. terminate with 0 and try to find end of it */
- interp[n] = 0;
- for (interp_start = interp; isspace(*interp_start) && interp_start < interp + n; interp_start++);
- for (cp = interp_start; cp < interp + n; cp++)
- if (*cp == 0 || isspace (*cp)) break;
- ch = *cp;
- *cp = 0;
-
- /* okay, lets try to load this instead. Call us recursively,
- * but leave out the argument-argument, so we can't get
- * into infinite recursion. Interpreter-Expansion is only
- * done the first time __load_seg() is called from
- * execve()
- */
- tmpcd = CurrentDir(ocd);
- u.u_is_root = oroot;
- seg = (struct my_seg *) __load_seg (interp_start, 0);
- CurrentDir(tmpcd);
- u.u_is_root = 0;
- *cp = ch;
- if (!seg)
- goto fd_close;
-
- /* in this case, set the argument as well.
- */
-
- /* first skip intergap whitespace */
- for (; cp < interp + n; cp++)
- if (!*cp || !isspace(*cp) || *cp == '\n')
- break;
-
- if (*cp && *cp != '\n')
- {
- /* we read a certain amount of bytes, but we
- * unconditionally stop when we hit newline
- */
- interp_path = cp;
-
- /* crop any further arguments, only ONE argument
- * is supported
- */
- for (; cp < interp + n; cp++)
- if (isspace (*cp))
- break;
- if (cp < interp + n)
- *cp = 0;
-
- *cp++ = ' ';
- }
- else
- cp = interp_path = interp_start;
-
- if (progname[0] != '/' && !index(progname, ':'))
- {
- BPTR lock;
-
- CurrentDir (ocd);
- lock = __llock (progname, ACCESS_READ);
- *cp++ = '/';
- if (lock && NameFromLock (lock, cp,
- MAXPATHLEN-(cp-interp)) == -1)
- {
- if ((cp = index(cp, ':')))
- *cp = '/';
- }
- else
- strcpy (cp - 1, progname);
- if (lock)
- __unlock (lock);
- }
- else
- strcpy (cp, progname);
-
- *args = (char *) syscall (SYS_strdup, interp_path);
- }
- }
- fd_close:
- syscall (SYS_close, fd);
- }
- }
- }
- CurrentDir (ocd);
- u.u_is_root = oroot;
-
- if (!seg)
- errno = ENOENT;
- return seg;
- }
-